Введение

Метилирование ДНК играет важную роль в регуляции экспрессии генов млекопитающих. Метилирование промотора, как правило, инактивирует экспрессию группы рядом расположенных генов, так как транскрипционные факторы не могут связаться с промотором вследствие как прямого стереохимического влияния метильной группы, так и изменения пространственной структуры хроматина. ДНК метилируется по остаткам цитозина по атому углерода в 5 положении с помощью специальных ферментов ДНК-метилтрансфераз. Цитозины метилируются в большинстве своём в специальных областях, богатыми CG-динуклеотидами. Такие области называют CpG-островки.

Исследование метилирования генов в онкологии помогает понять природу изменений в опухолевой ткани, выделить подтипы опухолей в соответствии с которыми можно подбирать терапевтические агенты. Методов, измеряющих метилирование ДНК, изобретено большое количество. В этом мастер-классе мы поговорим о бисульфитном секвенировании с уменьшенной представленностью (Reduced Representation Bisulfite Sequencing, RRBS).

Внимание: методы и подходы, рассказываемые в этом блокноте также применимы к полногеномному бисульфитному секвенированию (Whole Genome Bisulfite Sequencing, WGBS). Данный блокнот не претендует на истину в последней инстанции. Существуют альтернативные подходы к анализу и соответствующие программы и библиотеки.

RRBS

RRBS - бисульфитное секвенирование с уменьшенной представленностью. Оно более дешевое, чем секвенирование полного генома, но даёт информацию более, чем о 80% CpG островках человеческого генома. Достигается этот эффект за счёт обогащения библиотеки специальными рестриктазами (MspI, XmaI).

Библиотеки в процессе подготовки к секвенированию подвергают бисульфитной конверсии, т.е. обрабатывают раствором NaHSO3 . Суть этого шага, представлена на рисунке @ref(fig:bisulfite). Неметилированные цитозины при обработке бисульфитом натрия окисляются до урацила, который при ПЦР заменяется на тимин. Однако такому не подвержены метилированные цитозины. Следовательно, на том месте, где секвенатором получен тимин, был неметилированный цитозин, а процитанные цитозины - метилированные. Таким образом, можно измерять уровень метилирования в конкретном локусе ДНК.

Бисульфитная конверсия

Уровень метилирования рассчитывается по следующей формуле:

\[ methylation = \frac{C}{C+T} \]

Анализ данных

Последовательность обработки данных представлена рисунке @ref{fig:workflow} (Wreczycka et al. 2017).

Конвейер анализа данных RRBS

Контроль качества осуществляется с помощью стандартной программы FastQC.

library(DiagrammeR)
mermaid("
graph LR
    A{}-->B
")

Подготовка к работе

if (!require("BiocManager", quietly = TRUE))
    install.packages("BiocManager")

BiocManager::install(c("GEOquery","limma", "edgeR", "methylKit", "DMRcate"))
install.packages(c("stringr", "plyr"))

Импорт пакетов

Загрузка данных

# Чтобы загрузка не падала из-за временного ограничения на обмен между сервером и клиентом (вами)
options(timeout = max(300, getOption("timeout")))
options(download.file.method.GEOquery = "wget")

experiment = GEOquery::getGEO("GSE122799")[[1]]
untar("GSE122799/GSE122799_RAW.tar", exdir = "GSE122799/")
files = Sys.glob("GSE122799/*.bismark.cov.gz")
sample.include = which(phenotypes$characteristics_ch1.2 == "mtypehe: hiHER" | phenotypes$characteristics_ch1.2 == "mtypehe: hiLumAB")
phenotypes1 = phenotypes[sample.include, ]
files1 = files[sample.include]

Описательная статистика

getMethylationStats(raw_data[[2]],plot=TRUE,both.strands=FALSE)
getCoverageStats(raw_data[[2]],plot=TRUE,both.strands=FALSE)
for (sample in raw_data) {
  getMethylationStats(sample,plot=TRUE,both.strands=FALSE)
  Sys.sleep(5)
}
filtered = filterByCoverage(raw_data,lo.count=10,lo.perc=NULL,
                                hi.count=NULL,hi.perc=99.5)

Объединение результатов

meth=methylKit::unite(filtered, destrand=FALSE,min.per.group = 10L, mc.cores = 30)
#load("Unite_meth.RData")

Фильтрация

methMatrix = percMethylation(meth,rowids = T)
sds=matrixStats::rowSds(methMatrix,na.rm = T)
print(dim(methMatrix))
hist(sds,col="cornflowerblue",xlab="Std. dev. per CpG", breaks = 50)
dim(meth)
meth.filt = meth[sds > 10,]
dim(meth.filt)
shared = with(
  list(xx = ifelse(is.na(methMatrix), 0, 1), n = ncol(methMatrix)),
  ldply(
    1:(n-1),
    function(i)
    {
      # построим список каждый-с-каждым
      ddply(
        data.frame("i"=rep(i, n-i), "j"=(i+1):n),
        c("i","j"),
        function(row){
          i <- row[1,1]; j <- row[1,2];
          sum(xx[,i] * xx[,j]);
        });
    },.parallel=TRUE))
print(shared[shared$V1 <= 10000,])
miss =  function(x){
  sum(is.na(x))/(sum(!is.na(x))+sum(is.na(x)));
}

meth.filt.mat = percMethylation(meth.filt)
miss_x <- miss(methMatrix);
max_miss = 0.2
while (miss_x > max_miss) {
  xx = ifelse(is.na(meth.filt), 0, 1);
  
  # в скольких образцах есть данные по каждому локусу
  cvrg_c = apply(xx, 1, sum);
  cvrg_c_mean = mean(cvrg_c);
  cvrg_c_sd = sd(cvrg_c);
  filter_out_plan = nrow(meth.filt)*(miss_x - max_miss);
  
  cvrg_c_thrsh = cvrg_c[tail(head(order(cvrg_c), filter_out_plan) ,1)];
  cvrg_c_flt = cvrg_c <= cvrg_c_thrsh;
  
  meth.filt = meth.filt[!cvrg_c_flt, ];
  meth.filt.mat = percMethylation(meth.filt)
  miss_x = miss(meth.filt);
  
  cat(sprintf(
    "Filters out: #plan=%f, #fact=%d CpGs  ==>  Dim: %d x %d (samples x CpGs); miss(x) = %.4f \n",
    filter_out_plan, sum(cvrg_c_flt), dim(meth.filt.mat)[2], dim(meth.filt.mat)[1], miss_x ));
}

Анализ после фильтрации

png(width = 2400, height = 2400, filename = "correlation.png")
getCorrelation(meth.filt, plot = T)
dev.off()

Корреляция уровня метилирования между образцами

PCASamples(meth.filt)
PCASamples(meth.filt, screeplot = T)
tree = clusterSamples(meth.filt, dist = "correlation", method="ward.D2")
rect.hclust(tree, k = 2, border = "darkgreen")

DMR

meth.diff = calculateDiffMeth(meth.filt, overdispersion = "MN", 
                              adjust = "fdr", 
                              test = "F",
                              mc.cores = 30)
meth.diff$dif.met = ifelse(meth.diff$meth.diff <= -20 & meth.diff$qvalue <= 0.05,
                           "HYPO",
                           ifelse(meth.diff$meth.diff >= 20 & meth.diff$qvalue <= 0.05,
                                  "HYPER",
                                  "NO"))
methylKit::bedgraph(meth.diff,"meth_diff.bedgraph",col.name = "meth.diff")

signif.diff = getMethylDiff(meth.diff,qvalue = 0.05, difference = 20)
diff.df = do.call(cbind.data.frame, meth.diff@.Data)
colnames(diff.df) = c("chr", "start", "end", "strand", "pvalue", "qvalue", "meth.diff", "dif.met")
ggplot(data=diff.df, aes(x=meth.diff, y=-log10(pvalue), col=dif.met)) +
  geom_point() + 
  theme_minimal() +
  scale_color_manual(values=c("HYPO"="blue", "NO"="black", "HYPER"="green")) +
  geom_vline(xintercept=c(-20, 20), col="red") +
  geom_hline(yintercept=-log10(0.05), col="red")
rownames = paste(signif.diff$chr,signif.diff$start,signif.diff$end,sep = ".")
counts.dif.met = percMethylation(meth.filt,rowids = T)[rownames,]
ann_col_df = as.data.frame(phenotypes1$characteristics_ch1.2)
colnames(ann_col_df) = "Phenotype"
ann_col_df$Phenotype = as.factor(ann_col_df$Phenotype)
levels(ann_col_df$Phenotype) = c("hiHER","hiLumAB")
row.names(ann_col_df) = phenotypes1$geo_accession
ann_col_df

ann_colors = list(Phenotype = c("hiHER"="red", "hiLumAB"="orange"))

pheatmap::pheatmap(counts.dif.met,
                   color = colorRampPalette(c("white","cyan", "blue"))(100),
                   clustering_distance_rows = "manhattan",
                   clustering_distance_cols = "manhattan",
                   clustering_method = "ward.D2",
                   scale = "none",
                   annotation_col = ann_col_df,
                   annotation_colors = ann_colors,
                   treeheight_row = 100,
                   treeheight_col = 75)
require(genomation)
gene.obj=readTranscriptFeatures("refseq.hg19.bed.txt")
gene.parts = annotateWithGeneParts(as(signif.diff,"GRanges"),gene.obj)


cpg.obj=readFeatureFlank("cpgi.hg19.bed.txt",
                         feature.flank.name=c("CpGi","shores"))
diffCpGann=annotateWithFeatureFlank(as(signif.diff,"GRanges"),
                                    cpg.obj$CpGi,cpg.obj$shores,
                                    feature.name="CpGi",flank.name="shores")

library("org.Hs.eg.db")
promoters = gene.parts@dist.to.TSS[gene.parts@members[,1] == 1,]
cols <- c("ENTREZID","GENENAME","SYMBOL", "GENENAME")
annotation = AnnotationDbi::select(org.Hs.eg.db, keys=unique(promoters$feature.name), columns=cols, keytype="REFSEQ")
coord_promoters = cbind.data.frame(promoters, signif.diff[promoters$target.row,])
annotated_promoters = coord_promoters %>% dplyr::inner_join(annotation, by=c('feature.name'='REFSEQ'))

write.csv2(annotated_promoters, file = "promoters.csv")

Rgraphviz

genesOfInterest = annotated_promoters$meth.diff
names(genesOfInterest) = annotated_promoters$ENTREZID
genesOfInterest = sort(genesOfInterest,decreasing = T)
go <- clusterProfiler::enrichGO(gene=annotated_promoters$ENTREZID, 
             ont ="BP", 
             keyType = "ENTREZID", 
             pvalueCutoff = 0.1, 
             OrgDb = org.Hs.eg.db, 
             pAdjustMethod = "fdr",
             readable = T)
head(go)
dotplot(go)
Wreczycka, Katarzyna, Alexander Gosdschan, Dilmurat Yusuf, Björn Grüning, Yassen Assenov, and Altuna Akalin. 2017. “Strategies for Analyzing Bisulfite Sequencing Data.” Journal of Biotechnology 261 (November): 105–15. https://doi.org/10.1016/j.jbiotec.2017.08.007.
LS0tCnRpdGxlOiAi0JDQvdCw0LvQuNC3INC00LDQvdC90YvRhSBSUkJTLXNlcSIKYXV0aG9yOiAi0KHQvNC40YDQvdC+0LIg0JDQvdGC0L7QvSDQodC10YDQs9C10LXQstC40YciCmRhdGU6ICIyMi4wNS4yMDI0Igpib29rZG93bjo6aHRtbF9kb2N1bWVudDI6IGh0bWxfbm90ZWJvb2sKZmlnLWNhcHRpb246IHRydWUKYmlibGlvZ3JhcGh5OiByZWZlcmVuY2VzLmJpYgotLS0KCiMg0JLQstC10LTQtdC90LjQtQoK0JzQtdGC0LjQu9C40YDQvtCy0LDQvdC40LUg0JTQndCaINC40LPRgNCw0LXRgiDQstCw0LbQvdGD0Y4g0YDQvtC70Ywg0LIg0YDQtdCz0YPQu9GP0YbQuNC4INGN0LrRgdC/0YDQtdGB0YHQuNC4INCz0LXQvdC+0LIg0LzQu9C10LrQvtC/0LjRgtCw0Y7RidC40YUuINCc0LXRgtC40LvQuNGA0L7QstCw0L3QuNC1INC/0YDQvtC80L7RgtC+0YDQsCwg0LrQsNC6INC/0YDQsNCy0LjQu9C+LCDQuNC90LDQutGC0LjQstC40YDRg9C10YIg0Y3QutGB0L/RgNC10YHRgdC40Y4g0LPRgNGD0L/Qv9GLINGA0Y/QtNC+0Lwg0YDQsNGB0L/QvtC70L7QttC10L3QvdGL0YUg0LPQtdC90L7Qsiwg0YLQsNC6INC60LDQuiDRgtGA0LDQvdGB0LrRgNC40L/RhtC40L7QvdC90YvQtSDRhNCw0LrRgtC+0YDRiyDQvdC1INC80L7Qs9GD0YIg0YHQstGP0LfQsNGC0YzRgdGPINGBINC/0YDQvtC80L7RgtC+0YDQvtC8INCy0YHQu9C10LTRgdGC0LLQuNC1INC60LDQuiDQv9GA0Y/QvNC+0LPQviDRgdGC0LXRgNC10L7RhdC40LzQuNGH0LXRgdC60L7Qs9C+INCy0LvQuNGP0L3QuNGPINC80LXRgtC40LvRjNC90L7QuSDQs9GA0YPQv9C/0YssINGC0LDQuiDQuCDQuNC30LzQtdC90LXQvdC40Y8g0L/RgNC+0YHRgtGA0LDQvdGB0YLQstC10L3QvdC+0Lkg0YHRgtGA0YPQutGC0YPRgNGLINGF0YDQvtC80LDRgtC40L3QsC4g0JTQndCaINC80LXRgtC40LvQuNGA0YPQtdGC0YHRjyDQv9C+INC+0YHRgtCw0YLQutCw0Lwg0YbQuNGC0L7Qt9C40L3QsCDQv9C+INCw0YLQvtC80YMg0YPQs9C70LXRgNC+0LTQsCDQsiA1INC/0L7Qu9C+0LbQtdC90LjQuCDRgSDQv9C+0LzQvtGJ0YzRjiDRgdC/0LXRhtC40LDQu9GM0L3Ri9GFINGE0LXRgNC80LXQvdGC0L7QsiDQlNCd0Jot0LzQtdGC0LjQu9GC0YDQsNC90YHRhNC10YDQsNC3LiDQptC40YLQvtC30LjQvdGLINC80LXRgtC40LvQuNGA0YPRjtGC0YHRjyDQsiDQsdC+0LvRjNGI0LjQvdGB0YLQstC1INGB0LLQvtGR0Lwg0LIg0YHQv9C10YbQuNCw0LvRjNC90YvRhSDQvtCx0LvQsNGB0YLRj9GFLCDQsdC+0LPQsNGC0YvQvNC4IENHLdC00LjQvdGD0LrQu9C10L7RgtC40LTQsNC80LguINCi0LDQutC40LUg0L7QsdC70LDRgdGC0Lgg0L3QsNC30YvQstCw0Y7RgiBDcEct0L7RgdGC0YDQvtCy0LrQuC4KCtCY0YHRgdC70LXQtNC+0LLQsNC90LjQtSDQvNC10YLQuNC70LjRgNC+0LLQsNC90LjRjyDQs9C10L3QvtCyINCyINC+0L3QutC+0LvQvtCz0LjQuCDQv9C+0LzQvtCz0LDQtdGCINC/0L7QvdGP0YLRjCDQv9GA0LjRgNC+0LTRgyDQuNC30LzQtdC90LXQvdC40Lkg0LIg0L7Qv9GD0YXQvtC70LXQstC+0Lkg0YLQutCw0L3QuCwg0LLRi9C00LXQu9C40YLRjCDQv9C+0LTRgtC40L/RiyDQvtC/0YPRhdC+0LvQtdC5INCyINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjQuCDRgSDQutC+0YLQvtGA0YvQvNC4INC80L7QttC90L4g0L/QvtC00LHQuNGA0LDRgtGMINGC0LXRgNCw0L/QtdCy0YLQuNGH0LXRgdC60LjQtSDQsNCz0LXQvdGC0YsuINCc0LXRgtC+0LTQvtCyLCDQuNC30LzQtdGA0Y/RjtGJ0LjRhSDQvNC10YLQuNC70LjRgNC+0LLQsNC90LjQtSDQlNCd0JosINC40LfQvtCx0YDQtdGC0LXQvdC+INCx0L7Qu9GM0YjQvtC1INC60L7Qu9C40YfQtdGB0YLQstC+LiDQkiDRjdGC0L7QvCDQvNCw0YHRgtC10YAt0LrQu9Cw0YHRgdC1INC80Ysg0L/QvtCz0L7QstC+0YDQuNC8INC+INCx0LjRgdGD0LvRjNGE0LjRgtC90L7QvCDRgdC10LrQstC10L3QuNGA0L7QstCw0L3QuNC4INGBINGD0LzQtdC90YzRiNC10L3QvdC+0Lkg0L/RgNC10LTRgdGC0LDQstC70LXQvdC90L7RgdGC0YzRjiAoUmVkdWNlZCBSZXByZXNlbnRhdGlvbiBCaXN1bGZpdGUgU2VxdWVuY2luZywgUlJCUykuCgoqKirQktC90LjQvNCw0L3QuNC1Kio6KiDQvNC10YLQvtC00Ysg0Lgg0L/QvtC00YXQvtC00YssINGA0LDRgdGB0LrQsNC30YvQstCw0LXQvNGL0LUg0LIg0Y3RgtC+0Lwg0LHQu9C+0LrQvdC+0YLQtSDRgtCw0LrQttC1INC/0YDQuNC80LXQvdC40LzRiyDQuiDQv9C+0LvQvdC+0LPQtdC90L7QvNC90L7QvNGDINCx0LjRgdGD0LvRjNGE0LjRgtC90L7QvNGDINGB0LXQutCy0LXQvdC40YDQvtCy0LDQvdC40Y4gKFdob2xlIEdlbm9tZSBCaXN1bGZpdGUgU2VxdWVuY2luZywgV0dCUykuINCU0LDQvdC90YvQuSDQsdC70L7QutC90L7RgiDQvdC1INC/0YDQtdGC0LXQvdC00YPQtdGCINC90LAg0LjRgdGC0LjQvdGDINCyINC/0L7RgdC70LXQtNC90LXQuSDQuNC90YHRgtCw0L3RhtC40LguINCh0YPRidC10YHRgtCy0YPRjtGCINCw0LvRjNGC0LXRgNC90LDRgtC40LLQvdGL0LUg0L/QvtC00YXQvtC00Ysg0Log0LDQvdCw0LvQuNC30YMg0Lgg0YHQvtC+0YLQstC10YLRgdGC0LLRg9GO0YnQuNC1INC/0YDQvtCz0YDQsNC80LzRiyDQuCDQsdC40LHQu9C40L7RgtC10LrQuC4KCiMgUlJCUwoKUlJCUyAtINCx0LjRgdGD0LvRjNGE0LjRgtC90L7QtSDRgdC10LrQstC10L3QuNGA0L7QstCw0L3QuNC1INGBINGD0LzQtdC90YzRiNC10L3QvdC+0Lkg0L/RgNC10LTRgdGC0LDQstC70LXQvdC90L7RgdGC0YzRji4g0J7QvdC+INCx0L7Qu9C10LUg0LTQtdGI0LXQstC+0LUsINGH0LXQvCDRgdC10LrQstC10L3QuNGA0L7QstCw0L3QuNC1INC/0L7Qu9C90L7Qs9C+INCz0LXQvdC+0LzQsCwg0L3QviDQtNCw0ZHRgiDQuNC90YTQvtGA0LzQsNGG0LjRjiDQsdC+0LvQtdC1LCDRh9C10Lwg0L4gODAlIENwRyDQvtGB0YLRgNC+0LLQutCw0YUg0YfQtdC70L7QstC10YfQtdGB0LrQvtCz0L4g0LPQtdC90L7QvNCwLiDQlNC+0YHRgtC40LPQsNC10YLRgdGPINGN0YLQvtGCINGN0YTRhNC10LrRgiDQt9CwINGB0YfRkdGCINC+0LHQvtCz0LDRidC10L3QuNGPINCx0LjQsdC70LjQvtGC0LXQutC4INGB0L/QtdGG0LjQsNC70YzQvdGL0LzQuCDRgNC10YHRgtGA0LjQutGC0LDQt9Cw0LzQuCAoTXNwSSwgWG1hSSkuCgrQkdC40LHQu9C40L7RgtC10LrQuCDQsiDQv9GA0L7RhtC10YHRgdC1INC/0L7QtNCz0L7RgtC+0LLQutC4INC6INGB0LXQutCy0LXQvdC40YDQvtCy0LDQvdC40Y4g0L/QvtC00LLQtdGA0LPQsNGO0YIg0LHQuNGB0YPQu9GM0YTQuNGC0L3QvtC5INC60L7QvdCy0LXRgNGB0LjQuCwg0YIu0LUuINC+0LHRgNCw0LHQsNGC0YvQstCw0Y7RgiDRgNCw0YHRgtCy0L7RgNC+0LwgTmFIU09+M34gLiDQodGD0YLRjCDRjdGC0L7Qs9C+INGI0LDQs9CwLCDQv9GA0LXQtNGB0YLQsNCy0LvQtdC90LAg0L3QsCDRgNC40YHRg9C90LrQtSBcQHJlZihmaWc6YmlzdWxmaXRlKS4g0J3QtdC80LXRgtC40LvQuNGA0L7QstCw0L3QvdGL0LUg0YbQuNGC0L7Qt9C40L3RiyDQv9GA0Lgg0L7QsdGA0LDQsdC+0YLQutC1INCx0LjRgdGD0LvRjNGE0LjRgtC+0Lwg0L3QsNGC0YDQuNGPINC+0LrQuNGB0LvRj9GO0YLRgdGPINC00L4g0YPRgNCw0YbQuNC70LAsINC60L7RgtC+0YDRi9C5INC/0YDQuCDQn9Cm0KAg0LfQsNC80LXQvdGP0LXRgtGB0Y8g0L3QsCDRgtC40LzQuNC9LiDQntC00L3QsNC60L4g0YLQsNC60L7QvNGDINC90LUg0L/QvtC00LLQtdGA0LbQtdC90Ysg0LzQtdGC0LjQu9C40YDQvtCy0LDQvdC90YvQtSDRhtC40YLQvtC30LjQvdGLLiDQodC70LXQtNC+0LLQsNGC0LXQu9GM0L3Qviwg0L3QsCDRgtC+0Lwg0LzQtdGB0YLQtSwg0LPQtNC1INGB0LXQutCy0LXQvdCw0YLQvtGA0L7QvCDQv9C+0LvRg9GH0LXQvSDRgtC40LzQuNC9LCDQsdGL0Lsg0L3QtdC80LXRgtC40LvQuNGA0L7QstCw0L3QvdGL0Lkg0YbQuNGC0L7Qt9C40L0sINCwINC/0YDQvtGG0LjRgtCw0L3QvdGL0LUg0YbQuNGC0L7Qt9C40L3RiyAtINC80LXRgtC40LvQuNGA0L7QstCw0L3QvdGL0LUuINCi0LDQutC40Lwg0L7QsdGA0LDQt9C+0LwsINC80L7QttC90L4g0LjQt9C80LXRgNGP0YLRjCDRg9GA0L7QstC10L3RjCDQvNC10YLQuNC70LjRgNC+0LLQsNC90LjRjyDQsiDQutC+0L3QutGA0LXRgtC90L7QvCDQu9C+0LrRg9GB0LUg0JTQndCaLgoKIVvQkdC40YHRg9C70YzRhNC40YLQvdCw0Y8g0LrQvtC90LLQtdGA0YHQuNGPXShiaXN1bGZpdGUtY29udmVyc2lvbi5wbmcpeyNmaWctYmlzdWxmaXRlfQoK0KPRgNC+0LLQtdC90Ywg0LzQtdGC0LjQu9C40YDQvtCy0LDQvdC40Y8g0YDQsNGB0YHRh9C40YLRi9Cy0LDQtdGC0YHRjyDQv9C+INGB0LvQtdC00YPRjtGJ0LXQuSDRhNC+0YDQvNGD0LvQtToKCiQkCm1ldGh5bGF0aW9uID0gXGZyYWN7Q317QytUfQokJAoKIyDQkNC90LDQu9C40Lcg0LTQsNC90L3Ri9GFCgrQn9C+0YHQu9C10LTQvtCy0LDRgtC10LvRjNC90L7RgdGC0Ywg0L7QsdGA0LDQsdC+0YLQutC4INC00LDQvdC90YvRhSDQv9GA0LXQtNGB0YLQsNCy0LvQtdC90LAg0YDQuNGB0YPQvdC60LUgXEByZWZ7ZmlnOndvcmtmbG93fSBbQHdyZWN6eWNrYTIwMTddLgoKIVvQmtC+0L3QstC10LnQtdGAINCw0L3QsNC70LjQt9CwINC00LDQvdC90YvRhSBSUkJTXSh3b3JrZmxvdy5qcGcpeyNmaWctd29ya2Zsb3d9CgrQmtC+0L3RgtGA0L7Qu9GMINC60LDRh9C10YHRgtCy0LAg0L7RgdGD0YnQtdGB0YLQstC70Y/QtdGC0YHRjyDRgSDQv9C+0LzQvtGJ0YzRjiDRgdGC0LDQvdC00LDRgNGC0L3QvtC5INC/0YDQvtCz0YDQsNC80LzRiyBGYXN0UUMuCgpgYGB7cn0KbGlicmFyeShEaWFncmFtbWVSKQptZXJtYWlkKCIKZ3JhcGggTFIKICAgIEF7fS0tPkIKIikKYGBgCgojIyDQn9C+0LTQs9C+0YLQvtCy0LrQsCDQuiDRgNCw0LHQvtGC0LUKCmBgYHtyIGV2YWw9Rn0KaWYgKCFyZXF1aXJlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKGMoIkdFT3F1ZXJ5IiwibGltbWEiLCAiZWRnZVIiLCAibWV0aHlsS2l0IiwgIkRNUmNhdGUiKSkKaW5zdGFsbC5wYWNrYWdlcyhjKCJzdHJpbmdyIiwgInBseXIiKSkKYGBgCgojIyDQmNC80L/QvtGA0YIg0L/QsNC60LXRgtC+0LIKCmBgYHtyIGVjaG89VCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShHRU9xdWVyeSkKbGlicmFyeShsaW1tYSkKbGlicmFyeShlZGdlUikKbGlicmFyeShtZXRoeWxLaXQpCmxpYnJhcnkoRE1SY2F0ZSkKbGlicmFyeShkb1BhcmFsbGVsKQpsaWJyYXJ5KHBseXIpCmxpYnJhcnkoc3RyaW5ncikKYGBgCgojIyDQl9Cw0LPRgNGD0LfQutCwINC00LDQvdC90YvRhQoKYGBge3J9CiMg0KfRgtC+0LHRiyDQt9Cw0LPRgNGD0LfQutCwINC90LUg0L/QsNC00LDQu9CwINC40Lct0LfQsCDQstGA0LXQvNC10L3QvdC+0LPQviDQvtCz0YDQsNC90LjRh9C10L3QuNGPINC90LAg0L7QsdC80LXQvSDQvNC10LbQtNGDINGB0LXRgNCy0LXRgNC+0Lwg0Lgg0LrQu9C40LXQvdGC0L7QvCAo0LLQsNC80LgpCm9wdGlvbnModGltZW91dCA9IG1heCgzMDAsIGdldE9wdGlvbigidGltZW91dCIpKSkKb3B0aW9ucyhkb3dubG9hZC5maWxlLm1ldGhvZC5HRU9xdWVyeSA9ICJ3Z2V0IikKCmV4cGVyaW1lbnQgPSBHRU9xdWVyeTo6Z2V0R0VPKCJHU0UxMjI3OTkiKVtbMV1dCnBoZW5vdHlwZXMgPSBCaW9iYXNlOjpwRGF0YShleHBlcmltZW50KQpHRU9xdWVyeTo6Z2V0R0VPU3VwcEZpbGVzKCJHU0UxMjI3OTkiKQoKIyDQlNC70Y8g0YLRj9C20LXQu9GL0YUg0LLRi9GH0LjRgdC70LXQvdC40LkKcHJpbnQocGFzdGUoItCa0L7Quy3QstC+IENQVSIscGFyYWxsZWw6OmRldGVjdENvcmVzKCkpKQpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXM9cGFyYWxsZWw6OmRldGVjdENvcmVzKCktMikKYGBgCgpgYGB7cn0KdW50YXIoIkdTRTEyMjc5OS9HU0UxMjI3OTlfUkFXLnRhciIsIGV4ZGlyID0gIkdTRTEyMjc5OS8iKQpmaWxlcyA9IFN5cy5nbG9iKCJHU0UxMjI3OTkvKi5iaXNtYXJrLmNvdi5neiIpCmBgYAoKYGBge3J9CnNhbXBsZS5pbmNsdWRlID0gd2hpY2gocGhlbm90eXBlcyRjaGFyYWN0ZXJpc3RpY3NfY2gxLjIgPT0gIm10eXBlaGU6IGhpSEVSIiB8IHBoZW5vdHlwZXMkY2hhcmFjdGVyaXN0aWNzX2NoMS4yID09ICJtdHlwZWhlOiBoaUx1bUFCIikKcGhlbm90eXBlczEgPSBwaGVub3R5cGVzW3NhbXBsZS5pbmNsdWRlLCBdCmZpbGVzMSA9IGZpbGVzW3NhbXBsZS5pbmNsdWRlXQpgYGAKCmBgYHtyIGluY2x1ZGUgPSBGfQpyYXdfZGF0YSA9IG1ldGhSZWFkKGxvY2F0aW9uID0gYXMubGlzdChmaWxlczEpLAogICAgICAgICAgICAgICBwaXBlbGluZSA9ICJiaXNtYXJrQ292ZXJhZ2UiLAogICAgICAgICAgICAgICBzYW1wbGUuaWQ9YXMubGlzdChwaGVub3R5cGVzMSRnZW9fYWNjZXNzaW9uKSwKICAgICAgICAgICAgICAgYXNzZW1ibHk9ImhnMTkiLAogICAgICAgICAgICAgICB0cmVhdG1lbnQgPSBpZmVsc2UocGhlbm90eXBlczEkY2hhcmFjdGVyaXN0aWNzX2NoMS4yID09ICJtdHlwZWhlOiBoaUhFUiIsIDEsIDApLAogICAgICAgICAgICAgICBjb250ZXh0PSJDcEciKQpgYGAKCiMjINCe0L/QuNGB0LDRgtC10LvRjNC90LDRjyDRgdGC0LDRgtC40YHRgtC40LrQsAoKYGBge3J9CmdldE1ldGh5bGF0aW9uU3RhdHMocmF3X2RhdGFbWzJdXSxwbG90PVRSVUUsYm90aC5zdHJhbmRzPUZBTFNFKQpgYGAKCmBgYHtyfQpnZXRDb3ZlcmFnZVN0YXRzKHJhd19kYXRhW1syXV0scGxvdD1UUlVFLGJvdGguc3RyYW5kcz1GQUxTRSkKYGBgCgpgYGB7ciBldmFsPUZ9CmZvciAoc2FtcGxlIGluIHJhd19kYXRhKSB7CiAgZ2V0TWV0aHlsYXRpb25TdGF0cyhzYW1wbGUscGxvdD1UUlVFLGJvdGguc3RyYW5kcz1GQUxTRSkKICBTeXMuc2xlZXAoNSkKfQpgYGAKCmBgYHtyfQpmaWx0ZXJlZCA9IGZpbHRlckJ5Q292ZXJhZ2UocmF3X2RhdGEsbG8uY291bnQ9MTAsbG8ucGVyYz1OVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpLmNvdW50PU5VTEwsaGkucGVyYz05OS41KQpgYGAKCiMjINCe0LHRitC10LTQuNC90LXQvdC40LUg0YDQtdC30YPQu9GM0YLQsNGC0L7QsgoKYGBge3J9Cm1ldGg9bWV0aHlsS2l0Ojp1bml0ZShmaWx0ZXJlZCwgZGVzdHJhbmQ9RkFMU0UsbWluLnBlci5ncm91cCA9IDEwTCwgbWMuY29yZXMgPSAzMCkKI2xvYWQoIlVuaXRlX21ldGguUkRhdGEiKQpgYGAKCiMjINCk0LjQu9GM0YLRgNCw0YbQuNGPCgpgYGB7cn0KbWV0aE1hdHJpeCA9IHBlcmNNZXRoeWxhdGlvbihtZXRoLHJvd2lkcyA9IFQpCnNkcz1tYXRyaXhTdGF0czo6cm93U2RzKG1ldGhNYXRyaXgsbmEucm0gPSBUKQpwcmludChkaW0obWV0aE1hdHJpeCkpCmhpc3Qoc2RzLGNvbD0iY29ybmZsb3dlcmJsdWUiLHhsYWI9IlN0ZC4gZGV2LiBwZXIgQ3BHIiwgYnJlYWtzID0gNTApCmBgYAoKYGBge3J9CmRpbShtZXRoKQpgYGAKCmBgYHtyfQptZXRoLmZpbHQgPSBtZXRoW3NkcyA+IDEwLF0KYGBgCgpgYGB7cn0KZGltKG1ldGguZmlsdCkKYGBgCgpgYGB7cn0Kc2hhcmVkID0gd2l0aCgKICBsaXN0KHh4ID0gaWZlbHNlKGlzLm5hKG1ldGhNYXRyaXgpLCAwLCAxKSwgbiA9IG5jb2wobWV0aE1hdHJpeCkpLAogIGxkcGx5KAogICAgMToobi0xKSwKICAgIGZ1bmN0aW9uKGkpCiAgICB7CiAgICAgICMg0L/QvtGB0YLRgNC+0LjQvCDRgdC/0LjRgdC+0Log0LrQsNC20LTRi9C5LdGBLdC60LDQttC00YvQvAogICAgICBkZHBseSgKICAgICAgICBkYXRhLmZyYW1lKCJpIj1yZXAoaSwgbi1pKSwgImoiPShpKzEpOm4pLAogICAgICAgIGMoImkiLCJqIiksCiAgICAgICAgZnVuY3Rpb24ocm93KXsKICAgICAgICAgIGkgPC0gcm93WzEsMV07IGogPC0gcm93WzEsMl07CiAgICAgICAgICBzdW0oeHhbLGldICogeHhbLGpdKTsKICAgICAgICB9KTsKICAgIH0sLnBhcmFsbGVsPVRSVUUpKQpwcmludChzaGFyZWRbc2hhcmVkJFYxIDw9IDEwMDAwLF0pCmBgYAoKYGBge3J9Cm1pc3MgPSAgZnVuY3Rpb24oeCl7CiAgc3VtKGlzLm5hKHgpKS8oc3VtKCFpcy5uYSh4KSkrc3VtKGlzLm5hKHgpKSk7Cn0KCm1ldGguZmlsdC5tYXQgPSBwZXJjTWV0aHlsYXRpb24obWV0aC5maWx0KQptaXNzX3ggPC0gbWlzcyhtZXRoTWF0cml4KTsKbWF4X21pc3MgPSAwLjIKd2hpbGUgKG1pc3NfeCA+IG1heF9taXNzKSB7CiAgeHggPSBpZmVsc2UoaXMubmEobWV0aC5maWx0KSwgMCwgMSk7CiAgCiAgIyDQsiDRgdC60L7Qu9GM0LrQuNGFINC+0LHRgNCw0LfRhtCw0YUg0LXRgdGC0Ywg0LTQsNC90L3Ri9C1INC/0L4g0LrQsNC20LTQvtC80YMg0LvQvtC60YPRgdGDCiAgY3ZyZ19jID0gYXBwbHkoeHgsIDEsIHN1bSk7CiAgY3ZyZ19jX21lYW4gPSBtZWFuKGN2cmdfYyk7CiAgY3ZyZ19jX3NkID0gc2QoY3ZyZ19jKTsKICBmaWx0ZXJfb3V0X3BsYW4gPSBucm93KG1ldGguZmlsdCkqKG1pc3NfeCAtIG1heF9taXNzKTsKICAKICBjdnJnX2NfdGhyc2ggPSBjdnJnX2NbdGFpbChoZWFkKG9yZGVyKGN2cmdfYyksIGZpbHRlcl9vdXRfcGxhbikgLDEpXTsKICBjdnJnX2NfZmx0ID0gY3ZyZ19jIDw9IGN2cmdfY190aHJzaDsKICAKICBtZXRoLmZpbHQgPSBtZXRoLmZpbHRbIWN2cmdfY19mbHQsIF07CiAgbWV0aC5maWx0Lm1hdCA9IHBlcmNNZXRoeWxhdGlvbihtZXRoLmZpbHQpCiAgbWlzc194ID0gbWlzcyhtZXRoLmZpbHQpOwogIAogIGNhdChzcHJpbnRmKAogICAgIkZpbHRlcnMgb3V0OiAjcGxhbj0lZiwgI2ZhY3Q9JWQgQ3BHcyAgPT0+ICBEaW06ICVkIHggJWQgKHNhbXBsZXMgeCBDcEdzKTsgbWlzcyh4KSA9ICUuNGYgXG4iLAogICAgZmlsdGVyX291dF9wbGFuLCBzdW0oY3ZyZ19jX2ZsdCksIGRpbShtZXRoLmZpbHQubWF0KVsyXSwgZGltKG1ldGguZmlsdC5tYXQpWzFdLCBtaXNzX3ggKSk7Cn0KYGBgCgojIyDQkNC90LDQu9C40Lcg0L/QvtGB0LvQtSDRhNC40LvRjNGC0YDQsNGG0LjQuAoKYGBge3IgZXZhbD1GQUxTRX0KcG5nKHdpZHRoID0gMjQwMCwgaGVpZ2h0ID0gMjQwMCwgZmlsZW5hbWUgPSAiY29ycmVsYXRpb24ucG5nIikKZ2V0Q29ycmVsYXRpb24obWV0aC5maWx0LCBwbG90ID0gVCkKZGV2Lm9mZigpCmBgYAoKIVvQmtC+0YDRgNC10LvRj9GG0LjRjyDRg9GA0L7QstC90Y8g0LzQtdGC0LjQu9C40YDQvtCy0LDQvdC40Y8g0LzQtdC20LTRgyDQvtCx0YDQsNC30YbQsNC80LhdKGNvcnJlbGF0aW9uLnBuZykKCmBgYHtyfQpQQ0FTYW1wbGVzKG1ldGguZmlsdCkKYGBgCgpgYGB7cn0KUENBU2FtcGxlcyhtZXRoLmZpbHQsIHNjcmVlcGxvdCA9IFQpCmBgYAoKYGBge3J9CnRyZWUgPSBjbHVzdGVyU2FtcGxlcyhtZXRoLmZpbHQsIGRpc3QgPSAiY29ycmVsYXRpb24iLCBtZXRob2Q9IndhcmQuRDIiKQpyZWN0LmhjbHVzdCh0cmVlLCBrID0gMiwgYm9yZGVyID0gImRhcmtncmVlbiIpCmBgYAoKIyMgRE1SCgpgYGB7cn0KbWV0aC5kaWZmID0gY2FsY3VsYXRlRGlmZk1ldGgobWV0aC5maWx0LCBvdmVyZGlzcGVyc2lvbiA9ICJNTiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGp1c3QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3QgPSAiRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzID0gMzApCmBgYAoKYGBge3J9Cm1ldGguZGlmZiRkaWYubWV0ID0gaWZlbHNlKG1ldGguZGlmZiRtZXRoLmRpZmYgPD0gLTIwICYgbWV0aC5kaWZmJHF2YWx1ZSA8PSAwLjA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiSFlQTyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShtZXRoLmRpZmYkbWV0aC5kaWZmID49IDIwICYgbWV0aC5kaWZmJHF2YWx1ZSA8PSAwLjA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhZUEVSIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOTyIpKQptZXRoeWxLaXQ6OmJlZGdyYXBoKG1ldGguZGlmZiwibWV0aF9kaWZmLmJlZGdyYXBoIixjb2wubmFtZSA9ICJtZXRoLmRpZmYiKQoKc2lnbmlmLmRpZmYgPSBnZXRNZXRoeWxEaWZmKG1ldGguZGlmZixxdmFsdWUgPSAwLjA1LCBkaWZmZXJlbmNlID0gMjApCmBgYAoKYGBge3J9CmRpZmYuZGYgPSBkby5jYWxsKGNiaW5kLmRhdGEuZnJhbWUsIG1ldGguZGlmZkAuRGF0YSkKY29sbmFtZXMoZGlmZi5kZikgPSBjKCJjaHIiLCAic3RhcnQiLCAiZW5kIiwgInN0cmFuZCIsICJwdmFsdWUiLCAicXZhbHVlIiwgIm1ldGguZGlmZiIsICJkaWYubWV0IikKZ2dwbG90KGRhdGE9ZGlmZi5kZiwgYWVzKHg9bWV0aC5kaWZmLCB5PS1sb2cxMChwdmFsdWUpLCBjb2w9ZGlmLm1ldCkpICsKICBnZW9tX3BvaW50KCkgKyAKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiSFlQTyI9ImJsdWUiLCAiTk8iPSJibGFjayIsICJIWVBFUiI9ImdyZWVuIikpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygtMjAsIDIwKSwgY29sPSJyZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjA1KSwgY29sPSJyZWQiKQoKYGBgCgpgYGB7cn0Kcm93bmFtZXMgPSBwYXN0ZShzaWduaWYuZGlmZiRjaHIsc2lnbmlmLmRpZmYkc3RhcnQsc2lnbmlmLmRpZmYkZW5kLHNlcCA9ICIuIikKY291bnRzLmRpZi5tZXQgPSBwZXJjTWV0aHlsYXRpb24obWV0aC5maWx0LHJvd2lkcyA9IFQpW3Jvd25hbWVzLF0KYW5uX2NvbF9kZiA9IGFzLmRhdGEuZnJhbWUocGhlbm90eXBlczEkY2hhcmFjdGVyaXN0aWNzX2NoMS4yKQpjb2xuYW1lcyhhbm5fY29sX2RmKSA9ICJQaGVub3R5cGUiCmFubl9jb2xfZGYkUGhlbm90eXBlID0gYXMuZmFjdG9yKGFubl9jb2xfZGYkUGhlbm90eXBlKQpsZXZlbHMoYW5uX2NvbF9kZiRQaGVub3R5cGUpID0gYygiaGlIRVIiLCJoaUx1bUFCIikKcm93Lm5hbWVzKGFubl9jb2xfZGYpID0gcGhlbm90eXBlczEkZ2VvX2FjY2Vzc2lvbgphbm5fY29sX2RmCgphbm5fY29sb3JzID0gbGlzdChQaGVub3R5cGUgPSBjKCJoaUhFUiI9InJlZCIsICJoaUx1bUFCIj0ib3JhbmdlIikpCgpwaGVhdG1hcDo6cGhlYXRtYXAoY291bnRzLmRpZi5tZXQsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCJjeWFuIiwgImJsdWUiKSkoMTAwKSwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJtYW5oYXR0YW4iLAogICAgICAgICAgICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gIm1hbmhhdHRhbiIsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwKICAgICAgICAgICAgICAgICAgIHNjYWxlID0gIm5vbmUiLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBhbm5fY29sX2RmLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5fY29sb3JzLAogICAgICAgICAgICAgICAgICAgdHJlZWhlaWdodF9yb3cgPSAxMDAsCiAgICAgICAgICAgICAgICAgICB0cmVlaGVpZ2h0X2NvbCA9IDc1KQpgYGAKCmBgYHtyfQpyZXF1aXJlKGdlbm9tYXRpb24pCmdlbmUub2JqPXJlYWRUcmFuc2NyaXB0RmVhdHVyZXMoInJlZnNlcS5oZzE5LmJlZC50eHQiKQpnZW5lLnBhcnRzID0gYW5ub3RhdGVXaXRoR2VuZVBhcnRzKGFzKHNpZ25pZi5kaWZmLCJHUmFuZ2VzIiksZ2VuZS5vYmopCgoKY3BnLm9iaj1yZWFkRmVhdHVyZUZsYW5rKCJjcGdpLmhnMTkuYmVkLnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlLmZsYW5rLm5hbWU9YygiQ3BHaSIsInNob3JlcyIpKQpkaWZmQ3BHYW5uPWFubm90YXRlV2l0aEZlYXR1cmVGbGFuayhhcyhzaWduaWYuZGlmZiwiR1JhbmdlcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcGcub2JqJENwR2ksY3BnLm9iaiRzaG9yZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmUubmFtZT0iQ3BHaSIsZmxhbmsubmFtZT0ic2hvcmVzIikKYGBgCgpgYGB7cn0KCmxpYnJhcnkoIm9yZy5Icy5lZy5kYiIpCnByb21vdGVycyA9IGdlbmUucGFydHNAZGlzdC50by5UU1NbZ2VuZS5wYXJ0c0BtZW1iZXJzWywxXSA9PSAxLF0KY29scyA8LSBjKCJFTlRSRVpJRCIsIkdFTkVOQU1FIiwiU1lNQk9MIiwgIkdFTkVOQU1FIikKYW5ub3RhdGlvbiA9IEFubm90YXRpb25EYmk6OnNlbGVjdChvcmcuSHMuZWcuZGIsIGtleXM9dW5pcXVlKHByb21vdGVycyRmZWF0dXJlLm5hbWUpLCBjb2x1bW5zPWNvbHMsIGtleXR5cGU9IlJFRlNFUSIpCmNvb3JkX3Byb21vdGVycyA9IGNiaW5kLmRhdGEuZnJhbWUocHJvbW90ZXJzLCBzaWduaWYuZGlmZltwcm9tb3RlcnMkdGFyZ2V0LnJvdyxdKQphbm5vdGF0ZWRfcHJvbW90ZXJzID0gY29vcmRfcHJvbW90ZXJzICU+JSBkcGx5cjo6aW5uZXJfam9pbihhbm5vdGF0aW9uLCBieT1jKCdmZWF0dXJlLm5hbWUnPSdSRUZTRVEnKSkKCndyaXRlLmNzdjIoYW5ub3RhdGVkX3Byb21vdGVycywgZmlsZSA9ICJwcm9tb3RlcnMuY3N2IikKCgpgYGAKClJncmFwaHZpegoKYGBge3J9CmdlbmVzT2ZJbnRlcmVzdCA9IGFubm90YXRlZF9wcm9tb3RlcnMkbWV0aC5kaWZmCm5hbWVzKGdlbmVzT2ZJbnRlcmVzdCkgPSBhbm5vdGF0ZWRfcHJvbW90ZXJzJEVOVFJFWklECmdlbmVzT2ZJbnRlcmVzdCA9IHNvcnQoZ2VuZXNPZkludGVyZXN0LGRlY3JlYXNpbmcgPSBUKQpnbyA8LSBjbHVzdGVyUHJvZmlsZXI6OmVucmljaEdPKGdlbmU9YW5ub3RhdGVkX3Byb21vdGVycyRFTlRSRVpJRCwgCiAgICAgICAgICAgICBvbnQgPSJCUCIsIAogICAgICAgICAgICAga2V5VHlwZSA9ICJFTlRSRVpJRCIsIAogICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4xLCAKICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCAKICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiZmRyIiwKICAgICAgICAgICAgIHJlYWRhYmxlID0gVCkKYGBgCgpgYGB7cn0KaGVhZChnbykKYGBgCgpgYGB7cn0KZG90cGxvdChnbykKYGBgCg==